#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "util.h"
#include "file_sys.h"

#define DEFAULT_FLAGS (FOLLOW_SUBDIRS)
#define DEFAULT_MIN_FILE_SIZE 0
#define DEFAULT_MAX_FILE_SIZE 0
#define DEFAULT_MAX_DEPTH (~0)
#define DEFAULT_ATTR FLAG_(32)
#define DEFAULT_PATTERN "*"




static file_sys_attr default_attr = {DEFAULT_FLAGS, DEFAULT_MIN_FILE_SIZE,DEFAULT_MAX_FILE_SIZE,
				     DEFAULT_MAX_DEPTH, NULL};


void create_default_attr(file_sys_attr_t res) {
  memcpy(res,&default_attr, FS_ATTR_SIZE);
}




int check_attr(file_sys_t the_fs, char *path, struct stat* file_stats) {
  assert(path!=NULL);
  assert(file_stats!=NULL);
   
  file_sys_attr_t attr;
   
  if(the_fs->attr==NULL){
    attr = &default_attr;
  }
  else {
    attr = the_fs->attr;
  }

  if(S_ISDIR(file_stats->st_mode)) {
    if (is_flag_unset(attr->flags, FOLLOW_SUBDIRS) || attr->max_depth <= the_fs->depth)
      return 0;
    else 
      return 1;
     	
  }
   
  if(file_stats->st_size < attr->min_file_size)
    return 0;

  if(file_stats->st_size > attr->max_file_size && attr->max_file_size!=0)
    return 0;

   
  if(S_ISLNK(file_stats->st_mode) && is_flag_unset(attr->flags, FOLLOW_SYMB_LINKS))
    return 0;


  // TODO: implement regular expressions
   
  return 1;
}


void free_file_sys_attr(file_sys_attr_t e) {
  if(e!=NULL) {
    efree(e->pattern);
    free(e);
  }
}



/////////////////////////////////////////////
//
//       FILE_SYSTEM
//
/////////////////////////////////////////////


#define FILE_SYS_INIT_OK 0
#define FILE_SYS_INIT_ERROR (-1)






int init_file_sys(file_sys_t buf, char* dir, file_sys_attr_t attr) {
  assert(buf!=NULL);
  assert(dir!=NULL);
  
  buf->dir=dir;
  buf->child=NULL;
  buf->depth=0;
  
  buf->attr = attr;
  
  if ((buf->dip = opendir(dir)) == NULL) {
    perror("opendir");
    return FILE_SYS_INIT_ERROR;
  }
  
  int initerrno = errno;
  if((buf->curr = readdir(buf->dip))==NULL && (initerrno != errno)) {
    perror("readdir");
    return FILE_SYS_INIT_ERROR;
  }
  
  return FILE_SYS_INIT_OK;
}

void close_file_sys(file_sys_t e) {
  if(e!=NULL) {
    if (closedir(e->dip))
      perror("closedir");
    free(e);
  }
}


char *get_next_file(file_sys_t e) {
  assert(e!=NULL);
  char *result;
  if(e->child!=NULL) {
        
    result = get_next_file(e->child);
    if(result==NULL) {
      close_file_sys(e->child);
      chdir("..");
      e->child=NULL; 
    }
    else
    {
      return result;
}
  }
  
  if(e->curr == NULL)
    return NULL;
  result = e->curr->d_name;
  int initerrno = errno;
  if((e->curr = readdir(e->dip))==NULL && (initerrno != errno)) {
    perror("readdir");
    return NULL;
  }
  

  
  if(strcmp(result, ".") && strcmp(result, "..")) {
    struct stat file_stats;
    int sr = stat(result,&file_stats);
    if(sr)
    	perror("sr");
//    debug("%s with %d",result,sr);
    if (!sr && check_attr(e, result, &file_stats)) {
       
      if(S_ISDIR(file_stats.st_mode)) {
	e->child = (file_sys_t) emalloc(FS_SIZE);
	init_file_sys(e->child,result, e->attr);
	e->child->depth = e->depth+1;
	if(chdir(result)) {
	    close_file_sys(e->child);
	    e->child=NULL;
	}
      }
      else {
	return result;         
      }
    }
  }
  

  return get_next_file(e);
}
